#ifndef _KC_CONFIG_READER__H_KC_
#define _KC_CONFIG_READER__H_KC_

#include <stack>
#include <string>
#include <list>
#include <map>
#include <vector>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
#include <boost/noncopyable.hpp>
#include "../filter/Rule.h"
#include "../filter/Condition.h"
#include "../logs/Logger.h"
#include "ConfigError.h"
#include "HtmlTree.h"
#include "ConfigFileTagType.h"
#include "../filter/cond/Symbol.h"

namespace kc_sf {

	// elementy list HeaderType i Range
	enum HeaderType {
		FROM_HTYPE, TO_HTYPE
	};
	enum Range {
		HEADER_RANGE, BODY_RANGE, ALL_RANGE
	};

	/** Czytnik pliku konfiguracyjnego.
	 *
	 * Odczytuje plik konfiguracyjny zapisany w formacie xml.
	 * Pomija komentarze, rozpoznaje komentarze zagnieżdżone.
	 * Sprawdza poprawność danych zawartych w pliku. Pomija
	 * wpisy, których nie rozumie.
	 * Singleton
	 * */
	class ConfigFileReader : boost::noncopyable
	{
		public:

			static ConfigFileReader & getInstance();

			void operator()();

		private:

			// składowe
			HtmlTree c_tree;

			std::stack <ConfigFileTagType> c_states; // stos stanów, w jakich znajdował się czytnik

			// statyczne
			//static const int MaxErrors = 10;

			// metody
			bool checkTreeNode(Node<HtmlTag> &node);
			bool checkTag(const HtmlTag &tag, ConfigFileTagType type_of_tag);

			//	bool readRules(); // throws ConfigError
			//bool readPart(const std::string &last_tag = ""); // odczytaj część pliku konfiguracyjnego, zakończoną koncem taga last_tag

			/*typedef enum {
			 START_TAG, VALUE, END_TAG, NO_VALUE_TAG, _EOF
			 } XmlType;
			 XmlType readNextWord(std::string &to);
			 bool findCommentEnd();
			 */

			// spr czy tag ma poprawną zawartość; jeśli tak,
			// ustawia odpowiednią opcję i uzupełnia zawartość tag
			bool verificateUInt(HtmlTag &tag); // throws ConfigError
			bool verificateString(HtmlTag &tag); // throws ConfigError
			bool verificateTag(HtmlTag &tag); // throws ConfigError
			bool verificateNoValueTag(HtmlTag &tag); // throws ConfigError // czyli tag bez treści tylko same tagi-dzieci
			void verificateEndTag(HtmlTag &tag);

			//	bool actErrors(); // jeśli liczba błędów przekracza MaxErrors, ustawiane wartości domyślne

			// prywatne konstruktory - singleton
			ConfigFileReader();

			// statyczne
			static int mapHeaderTypeToInt(const std::string &value_str);
			static int mapRangeToInt(const std::string &value_str);

			ConfigFileTagType typeOf(const HtmlTag &tag) const;

			bool checkMaxSessionsTag(const HtmlTag &tag);
			bool checkMaxThreadsTag(const HtmlTag &tag);
			bool checkListenPortTag(const HtmlTag &tag);
			bool checkLogsFileTag(const HtmlTag &tag);
			bool checkLogsLevelTag(const HtmlTag &tag);
			bool checkSizeTag(const HtmlTag &tag);
			bool checkAttachmentsNrTag(const HtmlTag &tag);
			bool checkKeywordTag(const HtmlTag &tag);
			bool checkOccurencesTag(const HtmlTag &tag);
			bool checkNameTag(const HtmlTag &tag);
			bool checkRangeTag(const HtmlTag &tag);
			bool checkTypeTag(const HtmlTag &tag);
			bool checkSubtypeTag(const HtmlTag &tag);
			bool checkHeaderTag(const HtmlTag &tag);
			bool checkLocalpartTag(const HtmlTag &tag);
			bool checkDomainTag(const HtmlTag &tag);
		//	bool checkSrvDomainTag(const HtmlTag &tag);
			bool checkActionTag(const HtmlTag &tag);
			bool checkConfigurationTag(const HtmlTag &tag);
			bool checkRulesTag(const HtmlTag &tag);
			bool checkRuleTag(const HtmlTag &tag);
			bool checkConditionsTag(const HtmlTag &tag);
			bool checkMessageTag(const HtmlTag &tag);
		//	bool checkHeaderTag(const HtmlTag &tag);
			bool checkAttachmentTag(const HtmlTag &tag);
			bool checkAddressTag(const HtmlTag &tag);
			bool checkDateTag(const HtmlTag &tag);
		//	bool checkKeywordTag(const HtmlTag &tag);
			bool checkContentTypeTag(const HtmlTag &tag);

			Logic::Symbol * createAnrCondition( const HtmlTag &tag ) const;
			Logic::Symbol * createAttachmentCondition( const HtmlTag &tag ) const;
			Logic::Symbol * createDateCondition( const HtmlTag &tag ) const;
			Logic::Symbol * createFromCondition( const HtmlTag &tag ) const;
			Logic::Symbol * createKeywordCondition( const HtmlTag &tag ) const;
			Logic::Symbol * createResentCondition( const HtmlTag &tag ) const;
			Logic::Symbol * createSizeCondition( const HtmlTag &tag ) const;
			Logic::Symbol * createToCondition( const HtmlTag &tag ) const;
			Logic::Symbol * createValidCondition( const HtmlTag &tag ) const;

			template <typename T>
			T convertTagValue( const HtmlTag &tag ) const
			{
				T converted;

				try
				{
					converted = boost::lexical_cast <T >( tag.value() );
				}
				catch( boost::bad_lexical_cast & )
				{
					std::cerr << "Uwaga! Tag: " << tag.name() << " nie zawiera wartości liczbowej!"
						<< std::endl << "Zawartość: " << tag.value() << std::endl;
				}

				return converted;
			}
	};
}

#endif // _KC_CONFIG_READER__H_KC_
